require("utils")
require("tier1")
local utils = utils
local tier1 = tier1
local ipairs = ipairs
local pairs = pairs
local type = type
local math_floor = math.floor
local math_sqrt = math.sqrt

module("tier2")

local function apply_modifier(unit, tier1, type, value)
	local modifier = tier1.modifiers[type]
	if modifier and modifier ~= 0 then
		value = value * (1 + modifier / 100)
	end
	local rmods = tier1.ref_modifiers[type]
	if rmods then
		for _, mod in ipairs(rmods) do
			value = value + get(unit, tier1, mod.reference) * mod.multiplier
		end
	end
	return value
end

local statistics = {
	agility = true,
	intellect = true,
	spirit = true,
	stamina = true,
	strength = true,
}

local function default_get(unit, tier1, type)
	local value = tier1[type] or 0
	if value ~= 0 then
		value = apply_modifier(unit, tier1, type, value)
	end
	local rating = tier1.ratings[type]
	if rating and rating ~= 0 then
		value = (value or 0) + utils.GetRatingBonus(type, rating, unit.level)
	end
	if value and statistics[type] then
		local modifier = default_get(unit, tier1, "stats_p")
		value = value * (1 + modifier / 100)
	end
	return value or 0
end

local function null() end

local function get_defense_bonus(unit, tier1)
	local base = math_floor(default_get(unit, tier1, "skill_defense")) - unit.level * 5
	if base > 0 then
		return base * 0.04
	else
		return 0
	end
end

local AP_MULT = {
	melee = {
		WARRIOR = { 0, 2, },
		PALADIN = { 0, 2, },
		SHAMAN = { 0, 2, },
		ROGUE = { 1, 1, },
		MAGE = { 0, 2, },
		WARLOCK = { 0, 2, },
		PRIEST = { 0, 2, },
		HUNTER = { 1, 1, },
		DRUID = { 0, 2, },
	},
	ranged = {
		WARRIOR = { 0, 0, },
		PALADIN = { 0, 0, },
		SHAMAN = { 0, 0, },
		ROGUE = { 0, 0, },
		MAGE = { 0, 0, },
		WARLOCK = { 0, 0, },
		PRIEST = { 0, 0, },
		HUNTER = { 1, 0, },
		DRUID = { 0, 0, },
	},
}

local function get_spirit_manareg(unit, tier1)
	local ratio = utils.GetSpiritBaseRegen(unit)
	local spirit = default_get(unit, tier1, "spirit")
	local intellect = default_get(unit, tier1, "intellect")
	local value = 5 * (spirit * ratio * math_sqrt(intellect) + 0.001)
	return value
end

CALCULATORS = {
	maxhealth = function (unit, tier1)
		local stamina = default_get(unit, tier1, "stamina")
		local value = tier1.maxhealth + (stamina * 10 - 180)
		value = apply_modifier(unit, tier1, "maxhealth", value)
		
		return value
	end,
	maxmana = function (unit, tier1)
		local intellect = default_get(unit, tier1, "intellect")
		local value = tier1.maxmana + (intellect * 15 - 280)
		value = apply_modifier(unit, tier1, "maxmana", value)
		
		return value
	end,
	armor = function (unit, tier1)
		local agility = default_get(unit, tier1, "agility")
		local value = tier1.armor + agility * 2
		return default_get(unit, tier1, "base_armor") 
			+ apply_modifier(unit, tier1, "armor", value)
	end,
	dodge = function (unit, tier1)
		local value = default_get(unit, tier1, "dodge")
		local agility = default_get(unit, tier1, "agility")
		
		value = value + utils.GetBaseDataPoint("dodge", unit, agility)
		value = value + get_defense_bonus(unit, tier1)
		
		return value
	end,
	parry = function (unit, tier1)
		if not unit.can_parry then return 0 end
		local value = default_get(unit, tier1, "parry")
		value = value + get_defense_bonus(unit, tier1)
		
		return value
	end,
	block = function (unit, tier1)
		if not unit.can_block then return 0 end
		local value = default_get(unit, tier1, "block")
		value = value + get_defense_bonus(unit, tier1)
		
		return value
	end,
	
	blockvalue = function (unit, tier1)
		if not unit.can_block then return 0 end
		local value = default_get(unit, tier1, "blockvalue")

		local strength = default_get(unit, tier1, "strength")
		
		return value + strength / 20
	end,
	
	meleecrit = function (unit, tier1)
		local value = default_get(unit, tier1, "meleecrit")
		local agility = default_get(unit, tier1, "agility")
		
		value = value + utils.GetBaseDataPoint("crit", unit, agility)
		
		return value
	end,
	meleeattackpower = function (unit, tier1)
		local agility = default_get(unit, tier1, "agility")
		local strength = default_get(unit, tier1, "strength")
		local value = tier1.meleeattackpower + 
			agility * AP_MULT.melee[unit.class][1] + 
			strength * AP_MULT.melee[unit.class][2]
		value = apply_modifier(unit, tier1, "meleeattackpower", value)
		
		return value
	end,
	rangedcrit = function (unit, tier1)
		local value = default_get(unit, tier1, "rangedcrit")
		local agility = default_get(unit, tier1, "agility")
		
		value = value + utils.GetBaseDataPoint("crit", unit, agility)
		
		return value
	end,
	rangedattackpower = function (unit, tier1)
		local agility = default_get(unit, tier1, "agility")
		local strength = default_get(unit, tier1, "strength")
		local value = tier1.rangedattackpower + 
			agility * AP_MULT.ranged[unit.class][1] + 
			strength * AP_MULT.ranged[unit.class][2]
		value = apply_modifier(unit, tier1, "rangedattackpower", value)
		
		return value
	end,
	
	spellcrit = function (unit, tier1)
		local value = default_get(unit, tier1, "spellcrit")
		local intellect = default_get(unit, tier1, "intellect")
		
		value = value + utils.GetBaseDataPoint("spellcrit", unit, intellect)
		
		return value
	end,
	
	manareg = function (unit, tier1)
		-- mana regen when not in FSR.
		local spreg = get_spirit_manareg(unit, tier1)
		if not spreg then return end
			
		return default_get(unit, tier1, "manareg_fsr") + spreg
	end,
	manareg_fsr = function (unit, tier1)
		local spreg = get_spirit_manareg(unit, tier1)
		if not spreg then return end
		local value = default_get(unit, tier1, "manareg_fsr")
		local p = default_get(unit, tier1, "manareg_fsr_p")
		
		value = value + spreg * p
		
		return value
	end,
	-- remove some stat
	manareg_fsr_p = null,
	base_armor = null,
	stats_p = null,
}

function get(unit, tier1, stat)
	if type(tier1[stat]) == "number" then
		local calculator = CALCULATORS[stat] or default_get
		return calculator(unit, tier1, stat)
	end
end

function calc(unit)
	local tier1 = tier1.calc(unit)
	local tier2 = {}
	for stat, v in pairs(tier1) do
		tier2[stat] = get(unit, tier1, stat)
	end
	return tier2
end
